#include "sdk.h"

class cbTextInputStream : public wxTextInputStream {
  protected:
    bool m_allowMBconversion;
  public:
    cbTextInputStream( wxInputStream& s, const wxString &sep = _T( " \t" ), wxMBConv& conv = wxConvLocal )
      : wxTextInputStream( s, sep, conv ),
        m_allowMBconversion( true ) {
      memset( ( void* )m_lastBytes, 0, 10 );
    }
    ~cbTextInputStream() {}
    wxChar NextChar() {
      wxChar wbuf[2];
      memset( ( void* )m_lastBytes, 0, 10 );
      for( size_t inlen = 0; inlen < 9; inlen++ ) {
        m_lastBytes[inlen] = m_input.GetC();
        if( m_input.LastRead() <= 0 ) {
          return wxEOT;
        }
        if( m_allowMBconversion ) {
          int retlen = ( int ) m_conv->MB2WC( wbuf, m_lastBytes, 2 );
          if( retlen >= 0 ) {
            return wbuf[0];
          }
        } else
        { return m_lastBytes[inlen]; }
      }
      return wxEOT;
    }
    wxString ReadLine() {
      wxString line;
      while( m_input.CanRead() && !m_input.Eof() ) {
        wxChar c = NextChar();
        if( m_input.LastRead() <= 0 ) {
          break;
        }
        if( !m_input ) {
          break;
        }
        if( EatEOL( c ) ) {
          break;
        }
        line += c;
      }
      return line;
    }
};

int idTimerPollProcess = wxNewId();

BEGIN_EVENT_TABLE( PipedProcess, wxProcess )
  EVT_TIMER( idTimerPollProcess, PipedProcess::OnTimer )
  EVT_IDLE( PipedProcess::OnIdle )
END_EVENT_TABLE()

PipedProcess::PipedProcess( PipedProcess** pvThis, wxEvtHandler* parent, int id, bool pipe,
                            const wxString& dir, int index )
  : wxProcess( parent, id ),
    m_Parent( parent ),
    m_Id( id ),
    m_Pid( 0 ),
    m_Index( index ),
    m_pvThis( pvThis ) {
  const wxString &unixDir = UnixFilename( dir );
  if( !unixDir.empty() ) {
    wxSetWorkingDirectory( unixDir );
  }
  if( pipe ) {
    Redirect();
  }
}

PipedProcess::~PipedProcess() {
}

int PipedProcess::Launch( const wxString& cmd, M_Code_unused unsigned int pollingInterval ) {
  m_Pid = wxExecute( cmd, wxEXEC_ASYNC, this );
  return m_Pid;
}

void PipedProcess::SendString( const wxString& text ) {
  wxOutputStream* pOut = GetOutputStream();
  if( pOut ) {
    wxTextOutputStream sin( *pOut );
    wxString msg = text + _T( '\n' );
    sin.WriteString( msg );
  }
}

void PipedProcess::ForfeitStreams() {
  char buf[4096];
  if( IsErrorAvailable() ) {
    wxInputStream *in = GetErrorStream();
    while( in->Read( &buf, sizeof( buf ) ).LastRead() ) ;
  }
  if( IsInputAvailable() ) {
    wxInputStream *in = GetInputStream();
    while( in->Read( &buf, sizeof( buf ) ).LastRead() ) ;
  }
}

bool PipedProcess::HasInput() {
  if( IsErrorAvailable() ) {
    cbTextInputStream serr( *GetErrorStream() );
    wxString msg;
    msg << serr.ReadLine();
    CodeBlocksEvent event( cbEVT_PIPEDPROCESS_STDERR, m_Id );
    event.SetString( msg );
    event.SetX( m_Index );
    wxPostEvent( m_Parent, event );
    return true;
  }
  if( IsInputAvailable() ) {
    cbTextInputStream sout( *GetInputStream() );
    wxString msg;
    msg << sout.ReadLine();
    CodeBlocksEvent event( cbEVT_PIPEDPROCESS_STDOUT, m_Id );
    event.SetString( msg );
    event.SetX( m_Index );
    wxPostEvent( m_Parent, event );
    return true;
  }
  return false;
}

void PipedProcess::OnTerminate( int, int status ) {
  while( HasInput() ) ;
  CodeBlocksEvent event( cbEVT_PIPEDPROCESS_TERMINATED, m_Id );
  event.SetInt( status );
  event.SetX( m_Index );
  wxPostEvent( m_Parent, event );
  if( m_pvThis ) {
    *m_pvThis = nullptr;
  }
  delete this;
}

void PipedProcess::OnTimer( M_Code_unused wxTimerEvent& event ) {
  wxWakeUpIdle();
}

void PipedProcess::OnIdle( M_Code_unused wxIdleEvent& event ) {
  while( HasInput() ) ;
}
